using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using Network.Delegate;
using Network.Tcp.Awaitable;
using Network.Tcp;
using Network.Tcp.Pooling;
using Network.Tcp.NetSession;
using Network.Tcp.TcpConfig;
using Network.UtilityHelper;

namespace Network.Tcp.NetSession
{
    //Packet模式基类
    public class PackSessionBased : Session
    {
        private readonly byte[] _emptyHeart = new byte[] { 0, 0, 0, 0 };
        private int _packageRecvOffset;
        private readonly bool _iscompress;
        private readonly byte[] _headbuffer = new byte[4];
        internal int _isuchannel = 0;
        internal DateTime HeartTime { get; private set; } = DateTime.Now;
        #region 构造函数
        internal PackSessionBased(
            ConfigBase cfg,
            AwaiterPool awaiterPool,
            SessionPool sessionPool,
            NotifyEventHandler<CompleteNotify, Session> notifyEventHandler,
            LogHelper logHelper,
            NetEngineBased agent)
            : base(notifyEventHandler, cfg, awaiterPool, sessionPool, agent, logHelper)
        {
            _iscompress = cfg.CompressTransferFromPacket;
        }
        #endregion
        #region 更新session状态
        internal override void Attach(Socket socket)
        {
            if (socket == null)
                throw new ArgumentNullException("socket");

            lock (_opsLock)
            {
                HeartTime = DateTime.Now;
                _state = ConnectionState.Connected;
                _socket = socket;
                _startTime = DateTime.UtcNow;
                SetSocketOptions();
            }
        }
        #endregion
        #region 清除session状态
        internal override void Detach()
        {
            lock (_opsLock)
            {
                _socket = null;
                _state = ConnectionState.None;
                AppTokens = null;
                _packageRecvOffset = 0;
            }
        }
        #endregion
        #region 设置套接字参数
        private void SetSocketOptions()
        {
            _socket.ReceiveBufferSize = _configuration.ReceiveBufferSize;
            _socket.SendBufferSize = _configuration.SendBufferSize;
            _socket.ReceiveTimeout = (int)_configuration.ReceiveTimeout.TotalMilliseconds;
            _socket.SendTimeout = (int)_configuration.SendTimeout.TotalMilliseconds;
            _socket.NoDelay = _configuration.NoDelay;

            if (_configuration.KeepAlive)
                SetKeepAlive(_socket, 1, _configuration.KeepAliveInterval, _configuration.KeepAliveSpanTime);

            _socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, _configuration.ReuseAddress);
        }
        #endregion
        #region 设置在线探测参数
        private void SetKeepAlive(Socket sock, byte op, int interval, int spantime)
        {
            uint dummy = 0;
            byte[] inOptionValues = new byte[Marshal.SizeOf(dummy) * 3];
            BitConverter.GetBytes(op).CopyTo(inOptionValues, 0);//开启keepalive
            BitConverter.GetBytes((uint)interval).CopyTo(inOptionValues, Marshal.SizeOf(dummy));//多长时间开始第一次探测
            BitConverter.GetBytes((uint)spantime).CopyTo(inOptionValues, Marshal.SizeOf(dummy) * 2);//探测时间间隔
            sock.IOControl(IOControlCode.KeepAliveValues, inOptionValues, null);
        }
        #endregion
        #region 开始运行接收数据
        internal override void StartProcess()
        {
            var awaiter = _awaiterPool.Take();

            awaiter.Args.SetBuffer(_headbuffer, _packageRecvOffset, _headbuffer.Length);
            NetHelper.ReceiveAsync(_socket, awaiter, HeadProcess);
        }
        #endregion
        #region 包头处理
        private void HeadProcess(Awaiter awaiter, SocketError error)
        {
            //状态不正确时直接销毁session
            if (awaiter.Args.BytesTransferred == 0 ||
                error != SocketError.Success ||
                _state != ConnectionState.Connected ||
                _socket == null)
            {
                _logger.WriteLog("session_recv endtransfer state：" + _state.ToString() + " socket_error：" + error.ToString());
                EndTransfer(awaiter);
                return;
            }
            //更新心跳时间
            HeartTime = DateTime.Now;
            //接收偏移变更
            _packageRecvOffset += awaiter.Args.BytesTransferred;
            //当接收数据偏移大于等于包头长度时
            if (_packageRecvOffset >= 4)
            {
                int packBytesTransferred = BitConverter.ToInt32(_headbuffer, 0);

                if (packBytesTransferred < 0 || packBytesTransferred > _configuration.SendBufferSize)//长度包越界判断
                {
                    Close(true);
                    return;
                }
                //收到0长度的包
                if (packBytesTransferred == 0)
                {
                    if (_configuration._SessionIsService) //如果是服务端，则反馈心跳包
                        if (_isuchannel == 0)
                        {
                            var h_awaiter = _awaiterPool.Take();

                            //4个字节空包头
                            h_awaiter.Args.SetBuffer(_emptyHeart, 0, _emptyHeart.Length);
                            NetHelper.SendAsync(_socket, h_awaiter, (a, e) => _awaiterPool.Return(a));
                        }

                    //继续接收包头
                    _packageRecvOffset = 0;
                    //设置接收缓冲区
                    awaiter.Args.SetBuffer(_packageRecvOffset, _headbuffer.Length);
                    //开始接收，回调函数依然为接收包头，因为收到的是心跳
                    NetHelper.ReceiveAsync(_socket, awaiter, HeadProcess);
                    return;
                }
                //完整包缓冲区
                _completebuffer = new byte[packBytesTransferred];
                //接收偏移量归0
                _packageRecvOffset = 0;
                //设置接收缓冲区长度
                awaiter.Args.SetBuffer(_completebuffer, _packageRecvOffset, _completebuffer.Length);
                //开始接收，回调函数走分包
                NetHelper.ReceiveAsync(_socket, awaiter, PacketPartProcess);
            }
            else
            {
                //包头长度小于4，继续接收包头长度-已接收长度的数据
                awaiter.Args.SetBuffer(_packageRecvOffset, _headbuffer.Length - _packageRecvOffset);
                NetHelper.ReceiveAsync(_socket, awaiter, HeadProcess);
            }
        }
        #endregion
        #region 分包处理
        private void PacketPartProcess(Awaiter awaiter, SocketError error)
        {
            int bytesTransferred = awaiter.Args.BytesTransferred;
            //接收线程出错
            if (bytesTransferred == 0 ||
                error != SocketError.Success ||
                _state != ConnectionState.Connected ||
                _socket == null)
            {
                _logger.WriteLog("session_recv endtransfer state：" + _state.ToString() + " socket_error：" + error.ToString());
                EndTransfer(awaiter);
                return;
            }

            ReceiveBytesTransferred = bytesTransferred;
            //触发通知，正在接收数据
            _notifyEventHandler?.Invoke(CompleteNotify.OnDataReceiveing, this);
            //更新心跳时间
            HeartTime = DateTime.Now;
            //已接收包长度
            _packageRecvOffset += bytesTransferred;
            //如果已接收长度足够或已超过完整包长度则开始封包
            if (_packageRecvOffset >= _completebuffer.Length)
            {
                //压缩和通知事件
                PackageProcess();
                //收包长度偏移量归0
                _packageRecvOffset = 0;
                //开始接收包头
                awaiter.Args.SetBuffer(_headbuffer, _packageRecvOffset, _headbuffer.Length);
                NetHelper.ReceiveAsync(_socket, awaiter, HeadProcess);
            }
            else
            {
                //不够长度，继续接收
                awaiter.Args.SetBuffer(_packageRecvOffset, _completebuffer.Length - _packageRecvOffset);
                NetHelper.ReceiveAsync(_socket, awaiter, PacketPartProcess);
            }
        }
        #endregion
        #region 打包后发送通知
        private void PackageProcess()
        {
            if (_iscompress)
                _completebuffer = CompressHelper.Decompress(_completebuffer);

            _notifyEventHandler?.Invoke(CompleteNotify.OnDataReceived, this);
        }
        #endregion
        #region 关闭传输销毁session
        private void EndTransfer(Awaiter awaiter)
        {
            Close(true);
            _awaiterPool.Return(awaiter);
            _sessionPool.Return(this);
        }
        #endregion
        #region 发送数据
        public override void SendAsync(byte[] data) => SendAsync(data, 0, data.Length);

        public override void SendAsync(byte[] data, int offset, int lenght)
        {
            if (_socket == null)
                return;
            //根据选项压缩数据
            byte[] buffer = _iscompress 
                ? BuilderPack(CompressHelper.Compress(data, offset, lenght)) 
                : BuilderPack(data, offset, lenght);

            var awaiter = _awaiterPool.Take();
            awaiter.Args.SetBuffer(buffer, 0, buffer.Length);

            Interlocked.Increment(ref _isuchannel);
            NetHelper.SendAsync(_socket, awaiter, (a, e) =>
             {
                 Interlocked.Decrement(ref _isuchannel);
                 _awaiterPool.Return(awaiter);
                 SendTransferredBytes = a.Args.Buffer.Length;
                 _notifyEventHandler?.Invoke(CompleteNotify.OnSend, this);
             });

        }
        #endregion
        #region 封包
        private byte[] BuilderPack(byte[] data) => BuilderPack(data, 0, data.Length);
        private byte[] BuilderPack(byte[] data, int offset, int length)
        {
            byte[] buffer = new byte[data.Length + sizeof(int)];
            var bodyLen = BitConverter.GetBytes(data.Length); //数据包长度
            Array.Copy(bodyLen, 0, buffer, 0, bodyLen.Length); //包头4字节
            Array.Copy(data, offset, buffer, 4, length); //body
            return buffer;
        }
        #endregion
        #region 关闭连接
        public override void Close(bool notify)
        {
            lock (_opsLock)
            {
                if (_socket != null)
                {
                    try
                    {
                        _socket.Shutdown(SocketShutdown.Both);
                        _socket.Close();
                    }
                    catch (Exception e)
                    {
                        _logger.WriteLog("session关闭错误 info：" + e.Message);
                    }
                    finally
                    {
                        _socket = null;
                    }
                    _state = ConnectionState.Closed;

                    if (notify)
                    {
                        _notifyEventHandler?.Invoke(CompleteNotify.OnClosed, this);
                    }
                }
            }
        }
        #endregion
    }
}
